#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include "uart.h"

#define APPLICATION_PATH                "Application.bin"
#define UPGRADE_DATA_PACKAGES_LENGHT    0x40
#define UPGRADE_PACKAGES_LENGHT         0x40 + 0x04

typedef enum{

    MI_FALSE  =   0,
    MI_TRUE   =   1,

}MI_BOOL;

typedef unsigned char   MI_U8;
typedef unsigned short  MI_U16;

MI_U8 get_ver_cmd[6]            = {0x01,0x02,0x00,0x00,0x00,0x00};
MI_U8 update_cmd[6]             = {0x02,0x02,0x00,0x00,0x00,0x00};
MI_U8 file_size_cmd[8]          = {0x03,0x04,0x00,0x00,0x00,0x00,0x00,0x00};
MI_U8 file_package[UPGRADE_PACKAGES_LENGHT]         = {0x04,UPGRADE_DATA_PACKAGES_LENGHT};
MI_U8 update_complete_cmd[6]    = {0x05,0x02,0x00,0x00};
MI_U16 w_num = 0;

static MI_U8 isRunning = 0;
char r_data[256] = {0};
sem_t sem;

static MI_BOOL get_update_file_size(char * file_path,size_t *size)
{
    FILE *file;

    file = fopen(file_path,"rb");

    if (!file)
    {
        perror("get_update_file_size fopen error\n");
        return MI_FALSE;
    }
    fseek(file, 0L, SEEK_END);

    *size = ftell(file);

    fclose(file);
    return MI_TRUE;
}

static MI_U16 CRC16(MI_U8 * buf, MI_U16 len)
{
	MI_U16 i;
	MI_U16 crc = 0xffff;
 
	if (len == 0) {
		len = 1;
	}
	while (len--) {
        
		crc ^= *buf;
		for (i = 0; i<8; i++) 
		{            
			if (crc & 1) {               
				crc >>= 1;        
				crc ^= 0xA001;            
			}      
			else {               
				crc >>= 1;            
			}       
		}     
		buf++;
	}
	return(crc);
}

static MI_BOOL compare(MI_U8 *des,MI_U8 *src,int len)
{
    while (len--)
    {
        if (*des != *src)
        {
            return MI_FALSE;
        }

        des++;
        src++;
    }

    return MI_TRUE;
}

static void send_get_version_cmd(int fd)
{
    int len = sizeof(get_ver_cmd);
    int crc = CRC16(get_ver_cmd,len-2);
    get_ver_cmd[len-2] = crc & 0x00ff;
    get_ver_cmd[len-1] = ((crc >> 8) & 0x00ff);
    serialWrite(fd,get_ver_cmd,sizeof(get_ver_cmd));
}

static void send_enter_update_cmd(int fd)
{
    int len = sizeof(update_cmd);
    int crc = CRC16(update_cmd,len-2);
    update_cmd[len-2] = crc & 0x00ff;
    update_cmd[len-1] = ((crc >> 8) & 0x00ff);
    serialWrite(fd,update_cmd,sizeof(update_cmd));
}

static MI_BOOL send_update_file_size_cmd(int fd)
{
    int len = sizeof(file_size_cmd);
    size_t file_size = 0;
    get_update_file_size(APPLICATION_PATH,&file_size);

    file_size_cmd[2] = (file_size >> 24 & (0xff));
    file_size_cmd[3] = (file_size >> 16 & (0xff));
    file_size_cmd[4] = (file_size >> 8 & (0xff));
    file_size_cmd[5] = (file_size & (0xff));

    int crc = CRC16(file_size_cmd,len-2);
    file_size_cmd[len-2] = crc & 0x00ff;
    file_size_cmd[len-1] = ((crc >> 8) & 0x00ff);

    serialWrite(fd,file_size_cmd,sizeof(file_size_cmd));
    return MI_TRUE;
}

static MI_BOOL send_file_every_package(int fd)
{
    int len = sizeof(file_package);
    FILE *fp;
    size_t file_size;
    int package_num;
    MI_U8 package_buff[UPGRADE_DATA_PACKAGES_LENGHT] = {0};

    fp = fopen(APPLICATION_PATH,"rb");
    if (!fp)
    {
        perror("fopen error\n");
        return MI_FALSE;
    }
    get_update_file_size(APPLICATION_PATH,&file_size);

    if (file_size % UPGRADE_DATA_PACKAGES_LENGHT == 0 )
    {
        package_num = file_size / UPGRADE_DATA_PACKAGES_LENGHT;
    }
    else
    {
        package_num = (file_size / UPGRADE_DATA_PACKAGES_LENGHT) + 1;
    }

    printf("pageage_num == %d\n",package_num);
    while (!feof(fp)/* condition */)
    {
        /* code */
        int r_len = fread(package_buff,1,UPGRADE_DATA_PACKAGES_LENGHT,fp);
        
        // 最后读出来不满128 ，用0xff补全。
        if (r_len != UPGRADE_DATA_PACKAGES_LENGHT)
        {
            for (int i=r_len;i<UPGRADE_DATA_PACKAGES_LENGHT;i++)
            {
                package_buff[i] = 0xff;
            }
        }

        memcpy(&file_package[2],package_buff,sizeof(package_buff));

        int crc = CRC16(file_package,sizeof(file_package)-2);
        file_package[sizeof(file_package)-2] = crc & 0x00ff;
        file_package[sizeof(file_package)-1] = ((crc >> 8) & 0x00ff);


        usleep(30 * 1000);
        w_num++;

        printf("send package process == [%03d]\n", ((w_num * 100)/package_num));

#if DEBUG
        for(int i=0;i< len;i++)
        {
            printf("0x%02x  ",file_package[i]);
            if ((i+1) % 16 == 0)
                printf("\n");
        }
        printf("\n");

#endif
        memset(r_data,0,sizeof(r_data));
        serialWrite(fd,file_package,len);
        sem_wait(&sem);
#if DEBUG
        // for(int i=0;i< len;i++)
        // {
        //     printf("0x%02x  ",r_data[i]);
        //     if ((i+1) % 16 == 0)
        //         printf("\n");
        // }
        // printf("\n");


        // int status = compare(r_data,file_package,20);
        // if (status)
        // {
        //     printf("send_file_every_package and receive cmd success!\n");
        // }
        // else
        // {
        //     perror("send_file_every_package not equal receive cmd\n");
        // }
       
        //printf("read len == %d  w_num == %d \n",len,w_num);
 #endif        
    }
    
    fclose(fp);
    return MI_TRUE;
}

static MI_BOOL send_update_complete_cmd(int fd)
{
    int len = sizeof(update_complete_cmd);
    int crc = CRC16(update_complete_cmd,len-2);
    update_complete_cmd[len-2] = crc & 0x00ff;
    update_complete_cmd[len-1] = ((crc >> 8) & 0x00ff);
    serialWrite(fd,update_complete_cmd,sizeof(update_complete_cmd));

    return MI_TRUE;
}

void *uart_read_thread(void *arg)
{
    int fd = *((int *)arg);
    size_t  size ;        

    sem_wait(&sem);
    while(isRunning)
    {
        size = serialRead(fd,r_data,256); //阻塞方式去读

    #if DEBUG    
        if (size > 0)
        {
            for(int i=0;i<size;i++)
            {
                printf("0x%02x  ",r_data[i]);
            }
            printf("\n");
        }
    #endif    
        sem_post(&sem);
    }

    printf("uart_read_thread exit\n");
    pthread_exit(0);
}

int main(int argc,char *argv[])
{
    int fd = 0;
    int ret;
    char w_data[] = "hello world\n";
    MI_U16 crc = 0;
    MI_BOOL status;
    pthread_t m_read_thread ;
    size_t update_file_size;

    sem_init(&sem, 0, 0);
    fd = serialOpen("/dev/ttyUSB0",115200);

    if (fd > 0)
    {
        printf("open ttyUSB0 ok\n");
    }
    else
    {
        printf("open ttyUSB0 fail\n");
        return -1;
    }

    ret = pthread_create(&m_read_thread,NULL,uart_read_thread,&fd);
    if (ret)
    {
        perror("pthread_create error\n");
        return -1;
    }
    else
    {
        isRunning = 1;
        sem_post(&sem);
    }

    sleep(1);// 获取一下N32G430C8L7的版本号
    
    memset(r_data,0,sizeof(r_data));
    send_get_version_cmd(fd);
    sem_wait(&sem);

    printf("get version == %s\n",r_data);

    memset(r_data,0,sizeof(r_data));
    send_enter_update_cmd(fd);
    sem_wait(&sem);

    status = compare(r_data,update_cmd,sizeof(update_cmd));
    if (status)
    {
        printf("send_enter_update_cmd and receive cmd success!\n");
    }
    else
    {
        perror("send_enter_update_cmd not equal receive cmd\n");
    }

    get_update_file_size(APPLICATION_PATH,&update_file_size);
    printf("get update file size == %ld\n",update_file_size);

    memset(r_data,0,sizeof(r_data));
    send_update_file_size_cmd(fd);
    sem_wait(&sem);

    status = compare(r_data,file_size_cmd,sizeof(file_size_cmd));
    if (status)
    {
        printf("send_update_file_size_cmd and receive cmd success!\n");
    }
    else
    {
        perror("send_update_file_size_cmd not equal receive cmd\n");
    }

    send_file_every_package(fd);

    memset(r_data,0,sizeof(r_data));
    send_update_complete_cmd(fd);
    sem_wait(&sem);

    pthread_cancel(m_read_thread);
    isRunning = 0;

    pthread_join(m_read_thread,NULL);
    serialClose(fd);
    printf("raspberryPi App exit!\n");
}